﻿namespace TfsExtensions.Build.Activities
{
    using System;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Collections;
    using System.Text.RegularExpressions;
    using System.Text;
    using System.Globalization;
    using TfsExtensions.Build.Activities.Entities;
    using System.Activities;
    using System.Collections.Generic;
    using Microsoft.TeamFoundation.Build.Workflow.Tracking;
    using Microsoft.TeamFoundation.Build.Workflow.Activities;
    using Microsoft.TeamFoundation.Build.Client;

    public class UpdateAssemblyInfo : CodeActivity
    {
        public InArgument<IEnumerable<String>> AssemblyInfoFiles { get; set; }
        public InArgument<AssemblySettings> AssemblyMetadataSettings { get; set; }

        protected override void Execute(CodeActivityContext context)
        {
            var files = AssemblyInfoFiles.Get(context);
            foreach (var file in files)
            {
                FileInfo info = new FileInfo(file);
                if (!(info.Extension == ".cs" || info.Extension == ".vb"))
                {
                    throw new ArgumentException("Only CS or VB files are supported");
                }
            }

            var result = this.ExecuteInternal(context);
        }

        private string SurroundWithBrackets(string assemblyLine, string bracketLeft, string bracketRight)
        {
            if (string.IsNullOrEmpty(assemblyLine))
            {
                throw new ArgumentException("assemblyLine cannot be empty or null");
            }

            if (string.IsNullOrEmpty(bracketLeft))
            {
                throw new ArgumentException("bracketLeft cannot be empty or null");
            }

            if (string.IsNullOrEmpty(bracketRight))
            {
                throw new ArgumentException("bracketRight cannot be empty or null");
            }

            return string.Format(CultureInfo.InvariantCulture, "{0}{1}{2}", bracketLeft, assemblyLine, bracketRight);
        }

        private void EnsurePlaceholders(CodeActivityContext context)
        {
            string bracketLeft = string.Empty;
            string bracketRight = string.Empty;
            string csString = string.Empty;
            string assemblyName;
            string outString;

            bool foundAssemblyVersion = false;
            bool foundAssemblyFileVersion = false;
            bool foundAssemblyCompany = false;
            bool foundAssemblyProduct = false;
            bool foundAssemblyDescription = false;
            
            foreach (string taskItem in this.AssemblyInfoFiles.Get(context))
            {
                foundAssemblyVersion = false;
                foundAssemblyFileVersion = false;
                foundAssemblyCompany = false;
                foundAssemblyProduct = false;
                foundAssemblyDescription = false;

                var output = new StringBuilder();
                using (var reader = new StreamReader(taskItem))
                {
                    // CS or VB...?
                    if (taskItem.EndsWith(".cs", StringComparison.CurrentCultureIgnoreCase))
                    {
                        csString = "@";

                        bracketLeft = "[";
                        bracketRight = "]";
                        
                        assemblyName = "assembly";
                    }
                    else
                    {
                        csString = String.Empty;
                        bracketLeft = "<";
                        bracketRight = ">";

                        assemblyName = "Assembly";
                    }

                    try
                    {
                        while (!reader.EndOfStream)
                        {
                            string lineRead = reader.ReadLine().Trim();

                            if (lineRead.StartsWith(bracketLeft, StringComparison.Ordinal))
                            {
                                if (lineRead.IndexOf("AssemblyVersion", StringComparison.Ordinal) != -1)
                                {
                                    foundAssemblyVersion = true;
                                }
                                else if (lineRead.IndexOf("AssemblyFileVersion", StringComparison.Ordinal) != -1)
                                {
                                    foundAssemblyFileVersion = true;
                                }
                                else if (lineRead.IndexOf("AssemblyCompany", StringComparison.Ordinal) != -1)
                                {
                                    foundAssemblyCompany = true;
                                }
                                else if (lineRead.IndexOf("AssemblyProduct", StringComparison.Ordinal) != -1)
                                {
                                    foundAssemblyProduct = true;
                                }
                                else if (lineRead.IndexOf("AssemblyDescription", StringComparison.Ordinal) != -1)
                                {
                                    foundAssemblyDescription = true;
                                }
                            }
                        }
                    }
                    finally
                    {
                    }
                }
                if (AssemblyMetadataSettings.Get(context).AssemblyVersionDetails.IsAssemblyVersionSelected)
                {
                    if (!foundAssemblyVersion)
                    {
                        outString = this.SurroundWithBrackets(string.Format(CultureInfo.InvariantCulture, "{0}: AssemblyVersion(\"{1}\")", assemblyName, "3.0.0.0"), bracketLeft, bracketRight);
                        output.AppendLine(outString);
                    }

                    if (!foundAssemblyFileVersion)
                    {
                        outString = this.SurroundWithBrackets(string.Format(CultureInfo.InvariantCulture, "{0}: AssemblyFileVersion(\"{1}\")", assemblyName, "3.0.0.0"), bracketLeft, bracketRight);
                        output.AppendLine(outString);
                    }
                }

                if (AssemblyMetadataSettings.Get(context).AssemblyMetadataDetails.IsAssemblyMetadataSelected)
                {
                    if (!foundAssemblyCompany)
                    {
                        outString = this.SurroundWithBrackets(string.Format(CultureInfo.InvariantCulture, "{0}: AssemblyCompany(\"{1}\")", assemblyName, "Dummy"), bracketLeft, bracketRight);
                        output.AppendLine(outString);
                    }

                    if (!foundAssemblyProduct)
                    {
                        outString = this.SurroundWithBrackets(string.Format(CultureInfo.InvariantCulture, "{0}: AssemblyProduct(\"{1}\")", assemblyName, "Dummy"), bracketLeft, bracketRight);
                        output.AppendLine(outString);
                    }

                    if (!foundAssemblyDescription)
                    {
                        outString = this.SurroundWithBrackets(string.Format(CultureInfo.InvariantCulture, "{0}: AssemblyDescription(\"{1}\")", assemblyName, "Dummy"), bracketLeft, bracketRight);
                        output.AppendLine(outString);
                    }
                }

                File.SetAttributes(taskItem, FileAttributes.Normal);
                using (var writer = new StreamWriter(taskItem, true))
                {
                    if(!String.IsNullOrEmpty(output.ToString()))
                    {
                        LogHelper.LogMessage(context, "Ensuring placeholders by adding {0} in {1}", output.ToString(), taskItem);
                    }
                    writer.Write(output.ToString());
                    writer.Flush();
                }
            }
        }

        public bool ExecuteInternal(CodeActivityContext context)
        {
           
            StreamWriter streamWriter = null;
            EnsurePlaceholders(context);
            foreach (string item in this.AssemblyInfoFiles.Get(context))
            {
                UpdateAssemblyInfoHelper assemblyInfo = new UpdateAssemblyInfoHelper(item);
                LogHelper.LogMessage(context, BuildMessageImportance.Normal, "Updating assembly info for {0}", new object[] { item });

                if (AssemblyMetadataSettings.Get(context).AssemblyVersionDetails.IsAssemblyVersionSelected)
                {
                    assemblyInfo["AssemblyVersion"] = this.AssemblyMetadataSettings.Get(context).AssemblyVersionDetails.ToString();
                    assemblyInfo["AssemblyFileVersion"] = this.AssemblyMetadataSettings.Get(context).FileVersionDetails.ToString();
                }

                if (AssemblyMetadataSettings.Get(context).AssemblyMetadataDetails.IsAssemblyMetadataSelected)
                {
                    this.UpdateProperty(context, assemblyInfo, "AssemblyCompany", AssemblyMetadataSettings.Get(context).AssemblyMetadataDetails.CompanyName);
                    this.UpdateProperty(context, assemblyInfo, "AssemblyDescription", AssemblyMetadataSettings.Get(context).AssemblyMetadataDetails.ApplicationDescription);
                    this.UpdateProperty(context, assemblyInfo, "AssemblyProduct", AssemblyMetadataSettings.Get(context).AssemblyMetadataDetails.ApplicationName);
                }

                //var path = Path.GetTempFileName();
                using (streamWriter = new StreamWriter(item))
                {
                    assemblyInfo.Write(streamWriter);
                    //File.Copy(path, item, true);
                }
                
            }
            return true;
        }

        private void UpdateProperty(CodeActivityContext context,UpdateAssemblyInfoHelper assemblyInfo, string propertyName, string propertyValue)
        {
            if (!string.IsNullOrEmpty(propertyValue))
            {
                assemblyInfo[propertyName] = propertyValue;
                LogHelper.LogMessage(context, BuildMessageImportance.Normal, "\tUpdating {0} to \"{1}\"", new object[] { propertyName, propertyValue });
            }
        }
    }
}

